package com.gosophia.metadataEngine.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.Document;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.gosophia.commons.OrderCondition;
import com.gosophia.commons.Page;
import com.gosophia.commons.PropertyFilter;
import com.gosophia.commons.PropertyFilter.OperatorType;
import com.gosophia.commons.exception.BusinessException;
import com.gosophia.commons.persistence.HibernateRepository;
import com.gosophia.metadataEngine.commons.CreateXML;
import com.gosophia.metadataEngine.commons.SessionUtil;
import com.gosophia.metadataEngine.dao.BusinessDataDao;
import com.gosophia.metadataEngine.dao.DataSourceInfoDao;
import com.gosophia.metadataEngine.dao.MetaDataFiledDao;
import com.gosophia.metadataEngine.dao.MetaDataTableDao;
import com.gosophia.metadataEngine.entity.DataSourceInfo;
import com.gosophia.metadataEngine.entity.MetaDataField;
import com.gosophia.metadataEngine.entity.MetaDataTable;
import com.gosophia.metadataEngine.service.BusinessDataService;
import com.gosophia.metadataEngine.service.MetaDataFieldService;
import com.gosophia.metadataEngine.service.MetaDataTableService;
/**
 * 系统业务impl
 * @author mayue
 *
 */
@Service("businessDataService")
@Transactional
public class BusinessDataServiceImpl implements BusinessDataService {

    @Autowired
    private MetaDataFiledDao metaDataFieldDao;
    @Autowired
    private MetaDataTableDao metaDataTableDao;
    @Autowired
    private DataSourceInfoDao dataSourceInfoDao;

    @Autowired
    private BusinessDataDao businessDataDao;

    @Autowired
    private MetaDataTableService metaDataTableService;
    @Autowired
    private MetaDataFieldService metaDataFieldService;
    //元数据实体别名
    private static final StringBuilder ENTITYALIAS = new StringBuilder("_e");
    /**
     * 初始化所有sessionFactory
     */
	private void initMetadataSessionFactories1(){
		if(StaticConstant.COUNT ==0){
			sessionFactoriesInit();   
		}
		StaticConstant.COUNT += 1;
	}
	@Override
	public void initMetadataSessionFactories(){
		sessionFactoriesInit();   
	}
    
	private void sessionFactoriesInit() {
		for (DataSourceInfo dataSourceInfo : dataSourceInfoDao.getAll()){
    		List<MetaDataTable> metaDataTables = metaDataTableService.
    		                    findMetaDataTablesByDataSourceInfoId(dataSourceInfo.getDataSourceInfoId());
    		List<String> strXmls = new ArrayList<String>();
    		List<Long> hasDicTableIdList = new ArrayList<Long>();
    		for(MetaDataTable metaDataTable : metaDataTables){
    	        // 将字典表生成对应文件加载
    	        Long dicTableId = ifHasDictionaryTable(metaDataFieldService
    	                .findMetaDataFieldByTableId(metaDataTable.getMetaDataTableId()));
    	        if (dicTableId != null && !hasDicTableIdList.contains(dicTableId)) {
    	        	hasDicTableIdList.add(dicTableId);
    	            strXmls.add(CreateXML.changeDocToStr(CreateXML.getDocument(
    	                    dicTableId, metaDataTableDao, metaDataFieldDao)));
    	        }
    	        //根据元数据信息创建实体映射文件
    	        // 接入的数据表生成文件并加载，已经生成的映射文件不再次生成
    	        if(!hasDicTableIdList.contains(metaDataTable.getMetaDataTableId())){
        	        Document StrDoc = CreateXML.getDocument(metaDataTable.getMetaDataTableId(),
        	                metaDataTableDao, metaDataFieldDao);
        	        strXmls.add(CreateXML.changeDocToStr(StrDoc));
    	        }
    		}
    		String dataSourceName = dataSourceInfo.getName();
    		//根据数据源信息表Id创建 hibernte.cfg.xml文件
            Document configFile = CreateXML.createDataSourceXml(dataSourceInfo);
    		SessionUtil.buildSessionFacAndReloadConfiguration(dataSourceName, configFile, strXmls);
    	}
	}
	/**
     * 获取基础查询语句，其中，若某元数据字段类型为“OUT_FK”，则判定为引用外部数据字典，根据该地段refValue及字段privateName
     * 拼接查询条件，如sex.value as sex;
     * @param metaDataTableId
     * @return
     */
    private String getBaseHql(Long metaDataTableId) {

        // TableId不能为null
        Assert.notNull(metaDataTableId, "TableId不能为null");
        List<MetaDataField> metaDataFields = metaDataFieldService
                .findMetaDataFieldByTableIdListShow(metaDataTableId);
        MetaDataTable metaDataTable = metaDataTableService
                .findMetaDataTableById(metaDataTableId);

        // 没有为表配置字段
        Assert.notNull(metaDataFields, "没有为表配置字段");
        Assert.isTrue(metaDataFields.size() > 0, "没有为表配置字段");

        StringBuilder baseHql = new StringBuilder();
        StringBuilder hqlMap = new StringBuilder();
        baseHql.append("select new map(");

        // 拼接动态实体属性名
        for (MetaDataField metaDataField : metaDataFields) {
            if ("OUT_FK".equals(metaDataField.getFieldType())) {
                MetaDataField mdf = metaDataFieldDao.get(metaDataField
                        .getRefValue());
                if(mdf==null){
                    throw new RuntimeException("没有在元数据字段表中配置所引用列");
                }
                hqlMap.append(ENTITYALIAS + "." + metaDataField.getPrivateName() + "."
                        + mdf.getPrivateName());
            } else {
                hqlMap.append(ENTITYALIAS + "." + metaDataField.getPrivateName());
            }
            hqlMap.append(" as ");
            hqlMap.append(metaDataField.getPrivateName());
            hqlMap.append(",");

        }
        hqlMap.deleteCharAt(hqlMap.length() - 1);
        baseHql.append(hqlMap.toString());

        // 拼接动态实体名
        baseHql.append(") from ");
        baseHql.append(metaDataTable.getEntityClassName());
        baseHql.append(" " + ENTITYALIAS);
        return baseHql.toString();
    }

   
    /**
     * 使用dao时，根据metaDataTableId转换对应的sessionFactory
     * @param metaDataTableId
     */
    private void changeSessionFactory(Long metaDataTableId) {
        
        MetaDataTable metaDataTable = metaDataTableDao.get(metaDataTableId);
        Assert.notNull(metaDataTable, "metaDataTableId没有对应的MetaDataTable");
        
        DataSourceInfo dataSourceInfo = metaDataTable.getDataSourceInfo();
        Assert.notNull(dataSourceInfo, "metaDataTableId没有对应的数据源");
        
        String dataSourceName = dataSourceInfo.getName();

        businessDataDao.setSessionFactory(SessionUtil.getSessionFactory(dataSourceName));
    }
    @Override
    public Map<String, Object> findSystemDataDetail(Long id, Long metaDataTableId) {
        Assert.notNull(id, "id不能为null");
        Assert.notNull(metaDataTableId, "metaDataTableId不能为null");
        //调用初始化sessionFactorys
        initMetadataSessionFactories1();
        //TODO 临时测试
        String baseHql = getBaseHql(metaDataTableId);
        List<MetaDataField> metaDataFields = metaDataFieldService
                .findMetaDataFieldByTableId(metaDataTableId);
        StringBuffer idName = new StringBuffer();
        StringBuffer searchCond = new StringBuffer();
        for (MetaDataField m : metaDataFields) {
            if (m.getIsPk()) {
                idName.append(m.getPrivateName());
                break;
            }
        }
        searchCond.append(" where " + ENTITYALIAS + "."+idName + " = ?");

        StringBuffer hql = new StringBuffer();
        hql.append(baseHql);
        hql.append(searchCond);
        changeSessionFactory(metaDataTableId);

        return businessDataDao.findUniqueByHql(hql.toString(), id);
    }


    /**
     * 判断是否引用外部数据字典
     * 
     * @param metaDataFields
     * @return
     */
    private Long ifHasDictionaryTable(List<MetaDataField> metaDataFields) {
        Long theDicTableId = null;
        for (MetaDataField metaDataField : metaDataFields) {
            if ("OUT_FK".equals(metaDataField.getFieldType())) {
                MetaDataField mdf = metaDataFieldDao.get(metaDataField
                        .getRefValue());
                //判断mdf是否为null
                if(mdf != null){
                	theDicTableId = mdf.getMetaDataTable().getMetaDataTableId();
                }
                break;
            }
        }
        return theDicTableId;
    }
	@Override
	public Page<Map<String, Object>> findSystemDataAdvanceByPage(
			Long metaDataTableId, List<PropertyFilter> propertyFilters,
			Page<Map<String, Object>> page, List<OrderCondition> orderConditions) {
		Assert.notNull(metaDataTableId, "metaDataTableId不能为null");
        Assert.notNull(page,"page不能为null");
        Assert.isTrue(page.getPageSize()>0, "pageSize必须大于0");
        Assert.isTrue(page.getPageNo()>0,"pageNo必须大于0");
        //调用初始化sessionFactorys
        initMetadataSessionFactories1();
        //设置sessionFactory
        changeSessionFactory(metaDataTableId);
        //基础hql语句        
        String baseHql = getBaseHql(metaDataTableId);
        StringBuilder hql = new StringBuilder();
        // 查询条件
        String searchCond = HibernateRepository.changePropertyFiltersToNameHql(propertyFilters);
        //去除基础hql最后的别名
        int last_e_Index = baseHql.lastIndexOf("_e");
        hql.append(baseHql.substring(0, last_e_Index));
        hql.append(searchCond);
        // 添加排序hql语句
        String orderStr = HibernateRepository.ConvertOrderConditionToHql(orderConditions);
        if(orderStr !=null && !orderStr.isEmpty()){
        	hql.append(orderStr);
        }
        //为属性赋值
        Map<String, Object> parameValues = new HashMap<String, Object>();
        if(propertyFilters !=null && !propertyFilters.isEmpty()){
        	PropertyFilter[] propertys = new PropertyFilter[propertyFilters.size()];
            parameValues = HibernateRepository.getNameHqlParam2(propertyFilters.toArray(propertys));
        }
        return businessDataDao.findPageByHql(page, hql.toString(), parameValues);
	}
	@Override
	public Page<Map<String, Object>> findSystemDataSimpleByPage(
			Long metaDataTableId, Page<Map<String, Object>> page,
			String searchString, List<OrderCondition> orderConditions,List<PropertyFilter> propertyFilters) {
		//TODO
		Assert.notNull(metaDataTableId, "metaDataTableId不能为null");
        Assert.notNull(page,"page不能为null");
        Assert.isTrue(page.getPageSize()>0, "pageSize必须大于0");
        Assert.isTrue(page.getPageNo()>0,"pageNo必须大于0");
        //调用初始化sessionFactorys
        initMetadataSessionFactories1();
        //设置sessionFactory
        changeSessionFactory(metaDataTableId);
        String baseHql = getBaseHql(metaDataTableId);
        StringBuilder hql = new StringBuilder();
        List<MetaDataField> metaDataFields = metaDataFieldService
                .findMetaDataFieldByTableIdListShowNoHidden(metaDataTableId);
        propertyFilters = getSimpleSearchingConditions(searchString, propertyFilters, metaDataFields);
        // 查询条件
        String searchCond = HibernateRepository.changePropertyFiltersToNameHql(propertyFilters);
        //去除基础hql最后的别名
        int last_e_Index = baseHql.lastIndexOf("_e");
        hql.append(baseHql.substring(0, last_e_Index));
        //添加查询条件
        hql.append(searchCond);
        // 添加排序hql语句
        String orderStr = HibernateRepository.ConvertOrderConditionToHql(orderConditions);
        if(orderStr !=null && !orderStr.isEmpty()){
        	hql.append(orderStr);
        }
        //为属性赋值
        Map<String, Object> parameValues = new HashMap<String, Object>();
        if(propertyFilters !=null && !propertyFilters.isEmpty()){
        	PropertyFilter[] propertys = new PropertyFilter[propertyFilters.size()];
            parameValues = HibernateRepository.getNameHqlParam2(propertyFilters.toArray(propertys));
        }
        return businessDataDao.findPageByHql(page, hql.toString(), parameValues);
	}
	//获取模糊搜索查询调件
	private List<PropertyFilter> getSimpleSearchingConditions(String searchString,List<PropertyFilter> propertyFilters,List<MetaDataField> metaDataFields){
		if (searchString == null) {
			searchString = "";
		}
		PropertyFilter pf = null;
		List<PropertyFilter> pfs;
		if (propertyFilters == null) {
			pfs = new ArrayList<PropertyFilter>();
		} else {
			pfs = propertyFilters;
		}
		
		if(metaDataFields==null || metaDataFields.size()==0){
			return null;
		}
		for(MetaDataField metaDataField : metaDataFields){
			if(pf==null){
				pf = getPropertyFilterByField(searchString, metaDataField);
			}else{
				PropertyFilter newPf = getPropertyFilterByField(searchString, metaDataField);
				pf = pf.or(newPf);
			}
		}
		if (pf != null) {
			pfs.add(pf);
		}
		return pfs;
		
	}
	private PropertyFilter getPropertyFilterByField(String searchString,
			MetaDataField metaDataField) {
		PropertyFilter pf = new PropertyFilter();
        if(metaDataField.getFieldType().toLowerCase().equals("date")){
        	pf.setToCharFormat("yyyy-mm-dd");
        }
        if ("OUT_FK".equals(metaDataField.getFieldType())){
        	MetaDataField mdf = metaDataFieldDao.get(metaDataField
                    .getRefValue());
        	pf.setPropertyName(metaDataField.getPrivateName()+"."+mdf.getPrivateName());
        }else{
        	pf.setPropertyName(metaDataField.getPrivateName());
        }
        pf.setOperator(OperatorType.LIKE);
        pf.setPropertyValue(searchString);
		return pf;
	}
	@Override
	public List<List<Object>> findRefValues(Long metaDataTableId, Long metaDataFieldId) throws BusinessException {
		Assert.notNull(metaDataTableId,"metaDataTableId不能为null");
		Assert.notNull(metaDataFieldId,"metaDataFieldId不能为null");
		//调用初始化sessionFactorys
        initMetadataSessionFactories1();
        //设置sessionFactory
        changeSessionFactory(metaDataTableId);
		List<List<Object>> values = new ArrayList<List<Object>>();
		MetaDataTable metaTable = metaDataTableDao.get(metaDataTableId);
		if(metaTable==null){
			throw new BusinessException("ID为"+metaDataTableId+"表不存在", "JC2_YSJ_B_60");
		}
		MetaDataField dataField = metaDataFieldDao.get(metaDataFieldId);
		String searchField = "";
		if(dataField==null){
			throw new BusinessException("ID为"+dataField+"字段不存在", "JC2_YSJ_B_61");
		}
		if("OUT_FK".equalsIgnoreCase(dataField.getFieldType())){
			Assert.notNull(dataField.getRefValue());
			MetaDataField mdf = metaDataFieldDao.get(dataField
                    .getRefValue());
			searchField = dataField.getPrivateName()+"."+mdf.getPrivateName();
		}else{
			searchField = dataField.getPrivateName();
		}
		StringBuilder searchHql = new StringBuilder("select distinct new list(");
		searchHql.append(searchField);
		searchHql.append(")");
		searchHql.append(" from " + metaTable.getEntityClassName());
		values = businessDataDao.findListByHql(searchHql.toString());
		return values;
	}
	@Override
	public void deleteSystemDataById(Long dataId, Long metaDataTableId)
			throws BusinessException {
		Assert.notNull(dataId,"业务数据ID不能为null");
		Assert.notNull(metaDataTableId,"metaDataTableId不能为null");
		//调用初始化sessionFactorys
        initMetadataSessionFactories1();
        //设置sessionFactory
        changeSessionFactory(metaDataTableId);
        MetaDataTable metaTable = metaDataTableDao.get(metaDataTableId);
		if(metaTable==null){
			throw new BusinessException("ID为"+metaDataTableId+"表不存在", "JC2_YSJ_B_60");
		}
		String primaryName = metaDataFieldService.findPrimaryKeyName(metaDataTableId);
		String counthql = "select count(*) from " + metaTable.getEntityClassName() + " where " + primaryName +"="+dataId; 
		long count = businessDataDao.countHqlResult(counthql);
		if(count == 0){
			throw new BusinessException("ID为"+ dataId +"数据不存在或已被删除", "JC2_YSJ_B_62");
		}
		String deleteHql = "delete "+ metaTable.getEntityClassName() + " where " + primaryName +"="+dataId; 
		//执行删除操作
		executeUpdate(deleteHql);
		//businessDataDao.createQuery(deleteHql).executeUpdate();
	}
	@Override
	public void updateSystemData(Long dataId, Long metaDataTableId,
			Map<String, Object> values) throws BusinessException {
		Assert.notNull(dataId,"业务数据ID不能为null");
		Assert.notNull(metaDataTableId,"metaDataTableId不能为null");
		//调用初始化sessionFactorys
        initMetadataSessionFactories1();
        //设置sessionFactory
        changeSessionFactory(metaDataTableId);
        MetaDataTable metaTable = metaDataTableDao.get(metaDataTableId);
		if(metaTable==null){
			throw new BusinessException("ID为"+metaDataTableId+"表不存在", "JC2_YSJ_B_60");
		}
		String primaryName = metaDataFieldService.findPrimaryKeyName(metaDataTableId);
		String counthql = "select count(*) from " + metaTable.getEntityClassName() + " where " + primaryName +"="+dataId; 
		long count = businessDataDao.countHqlResult(counthql);
		if(count == 0){
			throw new BusinessException("ID为"+ dataId +"数据不存在或已被删除", "JC2_YSJ_B_62");
		}
		StringBuilder updateHql = new StringBuilder();
		updateHql.append("update ");
		updateHql.append(metaTable.getEntityClassName());
		updateHql.append(" set ");
		StringBuilder propertyStr = new StringBuilder();
		Set<String> keySet = values.keySet();
		Iterator<String> iterator = keySet.iterator();
		while(iterator.hasNext()){
			String key = iterator.next();
			String propertyType = getPropertyType(key,metaDataTableId);
			if("date".equalsIgnoreCase(propertyType) || "java.util.Date".equalsIgnoreCase(propertyType)){
				propertyStr.append(key +"=" + (Date)values.get(key)+", ");
			}else if("boolean".equalsIgnoreCase(propertyType) || "java.lang.Boolean".equalsIgnoreCase(propertyType)){
				propertyStr.append(key +"=" + (Boolean)values.get(key)+", ");
			}else if("int".equalsIgnoreCase(propertyType) || "java.lang.Integer".equalsIgnoreCase(propertyType)){
				propertyStr.append(key +"=" + (Integer)values.get(key)+", ");
			}else if("double".equalsIgnoreCase(propertyType) || "java.lang.Double".equalsIgnoreCase(propertyType)){
				propertyStr.append(key +"=" + (Double)values.get(key)+", ");
			}else if("long".equalsIgnoreCase(propertyType) || "java.lang.Long".equalsIgnoreCase(propertyType)){
				propertyStr.append(key +"=" + (Long)values.get(key)+", ");
			}else if("string".equalsIgnoreCase(propertyType) || "java.lang.String".equalsIgnoreCase(propertyType)){
				propertyStr.append(key +"='" + values.get(key)+"', ");
			}

		}
		propertyStr.deleteCharAt(propertyStr.lastIndexOf(","));
		updateHql.append(propertyStr);
		updateHql.append(" where " + primaryName +"="+dataId);
		//执行更新操作
		executeUpdate(updateHql.toString());
	}
	private void executeUpdate(String updateHql)
			throws HibernateException {
		businessDataDao.getSession().beginTransaction();
		businessDataDao.createQuery(updateHql).executeUpdate();
		businessDataDao.getSession().beginTransaction().commit();
	}
	//获取实体属性类型
	private String getPropertyType(String propertyName,Long metaDataTableId){
		Assert.notNull(propertyName,"propertyName不能为null");
		Assert.notNull(metaDataTableId,"metaDataTableId不能为null");
		MetaDataField metaDataField = metaDataFieldDao.getUniqueByHql("from MetaDataField where privateName =? and metaDataTable.metaDataTableId = ?", propertyName,metaDataTableId);
		if("OUT_FK".equalsIgnoreCase(metaDataField.getFieldType())){
			return "long";
		}else{
			return metaDataField.getFieldType();
		}
		
	}
	@Override
	public void addSystemData(Long metaDataTableId, Map<String, Object> values)
			throws BusinessException {
		Assert.notNull(metaDataTableId,"metaDataTableId不能为null");
		//调用初始化sessionFactorys
        initMetadataSessionFactories1();
        //设置sessionFactory
        changeSessionFactory(metaDataTableId);
        MetaDataTable metaTable = metaDataTableDao.get(metaDataTableId);
		if(metaTable==null){
			throw new BusinessException("ID为"+metaDataTableId+"表不存在", "JC2_YSJ_B_60");
		}
		if(values != null){
			businessDataDao.getSession().beginTransaction();
			businessDataDao.getSession().save(metaTable.getEntityClassName(), values);
			businessDataDao.getSession().beginTransaction().commit();
		}
	}

}
